package org.ceteca.explica.core.util;

import java.awt.Dimension;
import java.awt.geom.Point2D;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathFactory;

import org.ceteca.explica.client.ClientContext;
import org.ceteca.explica.client.gui.ImageUtils;
import org.ceteca.explica.core.ExplicaException;
import org.ceteca.explica.core.InterfaceErrors;
import org.ceteca.explica.core.InterfaceExplica;
import org.ceteca.explica.core.partners.AddressOT;
import org.eclipse.swt.graphics.Image;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

public class MapUtils {
	private static MapUtils instance;

	public MapUtils() {
		super();
	}

	public static MapUtils getInstance() {
		if (instance == null)
			instance = new MapUtils();

		return instance;
	}

	public Point2D geocode(AddressOT address) throws ExplicaException {
		try {
			StringBuffer urlBuffer = new StringBuffer();
			urlBuffer.append(InterfaceExplica.EXPLICA_URL_GOOGLE_GEOCODE);

			urlBuffer.append("?q=");
			urlBuffer.append(address.getAddress());
			urlBuffer.append("+" + address.getPostalCode());
			urlBuffer.append("+" + address.getCity());
			if (address.getState() != null)
				urlBuffer.append("+" + address.getState().getName());
			if (address.getCountry() != null)
				urlBuffer.append("+" + address.getCountry().getName());
			urlBuffer.append("&key=" + InterfaceExplica.EXPLICA_GOOGLE_APP_KEY);
			urlBuffer.append("&sensor=false");
			urlBuffer.append("&output=xml");

			URL mapRequestURL;

			mapRequestURL = new URL(urlBuffer.toString().replaceAll(" ", "%20"));

			Object contentObj = mapRequestURL.getContent();
			if (contentObj instanceof InputStream) {
				InputStream in = (InputStream) contentObj;
				StringBuffer mapResponse = new StringBuffer();
				int r = in.read();
				while (r >= 0) {
					mapResponse.append((char) r);
					r = in.read();
				}
				in.close();

				System.out.println(mapResponse.toString());

				ByteArrayInputStream bin = new ByteArrayInputStream(mapResponse
						.toString().getBytes());

				InputSource is = new InputSource(bin);

				DocumentBuilderFactory factory = DocumentBuilderFactory
						.newInstance();
				factory.setNamespaceAware(true);
				DocumentBuilder documentBuilder = factory.newDocumentBuilder();
				Document doc = documentBuilder.parse(is);

				bin.close();

				String textoResultadoConsulta = null;
				NodeList listaNodosNS = doc.getElementsByTagNameNS(
						"http://earth.google.com/kml/2.0", "code");

				if (listaNodosNS.getLength() > 0) {
					Node nodo = listaNodosNS.item(0);
					if (nodo instanceof Element)
						textoResultadoConsulta = nodo.getTextContent();
				}
				System.out.println("Resultado geolocalizacion:");
				System.out.println(textoResultadoConsulta);

				if (textoResultadoConsulta == null
						|| textoResultadoConsulta.isEmpty()
						|| !textoResultadoConsulta.equals("200")) {

					ArrayList<String> lParams = new ArrayList<String>();
					lParams.add(textoResultadoConsulta);
					ResultMessage res = ClientContext.getInstance()
							.getMsgCatalog().newResultMessage(
									InterfaceErrors.ERR_MAP_GEOCODING_ERROR);
					res.setLParams(lParams);
					throw new ExplicaException(res);
				}

				String textoCoordenadas = null;
				listaNodosNS = doc.getElementsByTagNameNS(
						"http://earth.google.com/kml/2.0", "coordinates");
				if (listaNodosNS.getLength() > 0) {
					Node nodo = listaNodosNS.item(0);
					if (nodo instanceof Element)
						textoCoordenadas = nodo.getTextContent();
				}

				System.out.println("Coordenadras geolocalizacion");
				System.out.println(textoCoordenadas);

				return Converter.getInstance().parseCoordinates(
						textoCoordenadas);

			} else {

				ResultMessage res = ClientContext.getInstance().getMsgCatalog()
						.newResultMessage(
								InterfaceErrors.ERR_MAP_GEOCODING_ERROR);
				throw new ExplicaException(res);
			}

		} catch (MalformedURLException me) {
			ResultMessage res = ClientContext.getInstance().getMsgCatalog()
					.newResultMessage(InterfaceErrors.ERR_MAP_URL_ERROR, me);
			throw new ExplicaException(res);

		} catch (IOException e) {
			ResultMessage res = ClientContext.getInstance().getMsgCatalog()
					.newResultMessage(InterfaceErrors.ERR_MAP_IO_ERROR, e);
			throw new ExplicaException(res);
		} catch (ParserConfigurationException e) {
			ResultMessage res = ClientContext.getInstance().getMsgCatalog()
					.newResultMessage(InterfaceErrors.ERR_MAP_PARSE_ERROR, e);
			throw new ExplicaException(res);
		} catch (SAXException e) {
			ResultMessage res = ClientContext.getInstance().getMsgCatalog()
					.newResultMessage(InterfaceErrors.ERR_MAP_PARSE_ERROR, e);
			throw new ExplicaException(res);
		}

	}

	public URL getGoogleStaticMapURL(Point2D coordinates, Dimension size,
			int zoom) throws ExplicaException {
		URL mapRequestURL = null;
		try {
			StringBuffer urlBuffer = new StringBuffer();
			urlBuffer.append(InterfaceExplica.EXPLICA_URL_GOOGLE_MAPS);
			urlBuffer.append("?center=" + coordinates.getX() + ","
					+ coordinates.getY());
			urlBuffer.append("&zoom=" + zoom);
			urlBuffer.append("&size=" + size.height+ "x"
					+ size.width);
			urlBuffer.append("&key=" + InterfaceExplica.EXPLICA_GOOGLE_APP_KEY);
			urlBuffer.append("&sensor=false");
			mapRequestURL = new URL(urlBuffer.toString().replaceAll(" ", "%20"));
			return mapRequestURL;

		} catch (MalformedURLException e) {
			ResultMessage res = new ResultMessage(
					InterfaceErrors.ERR_MAP_URL_ERROR, e);
			throw new ExplicaException(res);
		}

	}

	public URL getYahooMapImageURL(AddressOT address, int imgWidt,
			int imgHeight, int zoom) throws Exception {
		StringBuffer urlBuffer = new StringBuffer();
		urlBuffer.append(InterfaceExplica.EXPLICA_URL_YAHOO_MAPS);
		urlBuffer.append("?appid=" + InterfaceExplica.EXPLICA_KEY_YAHOO_APP);
		urlBuffer.append("&street=" + address.getAddress());
		urlBuffer.append("&city=" + address.getCity());
		urlBuffer.append("&zoom=" + zoom);
		urlBuffer.append("&image_width=" + imgWidt);
		urlBuffer.append("&image_height=" + imgHeight);

		URL mapRequestURL = new URL(urlBuffer.toString().replaceAll(" ", "%20"));

		Object contentObj = mapRequestURL.getContent();
		if (contentObj instanceof InputStream) {
			// Read the response XML from Yahoo!Maps
			InputStream in = (InputStream) contentObj;
			StringBuffer mapResponse = new StringBuffer();
			int r = in.read();
			while (r >= 0) {
				mapResponse.append((char) r);
				r = in.read();
			}
			in.close();

			// Using XPath to get the URL to the map image from the XML text

			XPath xpath = XPathFactory.newInstance().newXPath();
			String expression = "/Result/text()";
			ByteArrayInputStream bin = new ByteArrayInputStream(mapResponse
					.toString().getBytes());
			String result = xpath.evaluate(expression, new InputSource(bin));
			bin.close();

			bin = new ByteArrayInputStream(mapResponse.toString().getBytes());
			String message = xpath.evaluate("/Message/text()", new InputSource(
					bin));
			if (message != null && !message.isEmpty()) {
				ResultMessage res = new ResultMessage(
						OperationResult.RESULT_ERROR);
				res.setResMessage(message);
				bin.close();
				throw new ExplicaException(res);

			}
			bin.close();
			return new URL(result);

		} else {

			return null;
		}

	}

	public Image getYahooMapImage(AddressOT address, int imgWidt,
			int imgHeight, int zoom) throws Exception {
		URL url;

		url = getYahooMapImageURL(address, imgWidt, imgHeight, zoom);

		if (url == null)
			return null;
		else {
			return (Image) ImageUtils.getInstance().loadImage(url);
		}
	}

	public Image getGoogleMapImage(AddressOT address, Dimension size, int zoom)
			throws ExplicaException {
		URL url;
		Point2D coordinates = geocode(address);
		url = getGoogleStaticMapURL(coordinates, size, zoom);
		System.out.println("URL Map: "+url);
		if (url == null) {
			ResultMessage res = new ResultMessage(
					InterfaceErrors.ERR_MAP_URL_ERROR);
			throw new ExplicaException(res);
		} else
			return ImageUtils.getInstance().loadImage(url);
	}

	public Image getGoogleMapImage(Point2D c, Dimension size, int zoom)
			throws ExplicaException {
		URL url;
		url = getGoogleStaticMapURL(c, size, zoom);
		if (url == null) {
			ResultMessage res = new ResultMessage(
					InterfaceErrors.ERR_MAP_URL_ERROR);
			throw new ExplicaException(res);
		} else
			return ImageUtils.getInstance().loadImage(url);
	}

}
